package com.weijianhuawen.wjblog.jwt.filter;

import com.weijianhuawen.wjblog.jwt.exception.UsernameOrPasswordNullException;
import com.weijianhuawen.wjblog.jwt.helper.JwtTokenHelper;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.security.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Service;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

/**
 * @version: java version 8
 * @Author: weijianhuawen
 * @description: 校验token
 * @date: 2024-10-17 1:01
 */
@Slf4j
public class TokenAuthenticationFilter extends OncePerRequestFilter {
    //jwt token 生成与解析器
    @Resource
    private JwtTokenHelper jwtTokenHelper;

    //用户信息验证服务
    @Resource
    private UserDetailsService userDetailsService;

    @Resource
    private AuthenticationEntryPoint authenticationEntryPoint;

    @Value("${jwt.tokenPrefix}")
    private String tokenPrefix;

    @Value("${jwt.tokenHeaderKey}")
    private String tokenHeaderKey;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //获取请求url
        String reqUrl = request.getRequestURI();
        if (reqUrl.startsWith("/admin")) {
            //从请求头中获取token字段
            String header = request.getHeader(tokenHeaderKey);

            // 判断是否以Bearer开头
            if (StringUtils.startsWith(header, tokenPrefix)) {
                //截取token令牌
                String token = StringUtils.substring(header, 7);
                log.info("token: {}", token);

                //验证token是否为空
                if (StringUtils.isNotBlank(token)) {
                    try {
                        //校验token是否有效 如果抛异常说明无效
                        jwtTokenHelper.validateToken(token);
                    } catch (SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException e) {
                        // 抛出异常，统一让 AuthenticationEntryPoint 处理响应参数
                        authenticationEntryPoint.commence(request, response, new AuthenticationServiceException("token不可用"));
                        return;
                    } catch (ExpiredJwtException e) {
                        //token过期
                        authenticationEntryPoint.commence(request, response, new AuthenticationServiceException("token 已过期"));
                        return;
                    }
                    //解析token中的用户名
                    String username = jwtTokenHelper.getUsernameByToken(token);
                    if (StringUtils.isNotBlank(username)
                            && Objects.isNull(SecurityContextHolder.getContext().getAuthentication())) {
                        //根据用户名获取用户详细信息
                        UserDetails userDetails = userDetailsService.loadUserByUsername(username);

                        //将用户信息存入authentication
                        UsernamePasswordAuthenticationToken authenticationToken =
                                new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                        //将authentication 存入ThreadLocal，方便后续获取用户信息
                        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                    }
                }
            }
        }
        //执行过滤器
        filterChain.doFilter(request, response);
    }
}
